home *** CD-ROM | disk | FTP | other *** search
- /* Z80 Emulator
- Copyright (C) 1995 G.Woigk
-
- This file is part of Mac Spectacle and it is free software
- See application.c for details
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
- based on fMSX; Copyright (C) Marat Fayzullin 1994,1995
-
- 28.Mrz.95 Started work on this file KIO !
- 01.Apr.95 Finished, yee it works! KIO !
- 06.Apr.95 Removed "final bug": im2 interrupt handling KIO !
- 12.Jun.95 revised EI_WITH_DELAY handling KIO !
- 12.Jun.95 all Info-Calls and profiling added KIO !
- 12.Jun.95 exact r register emulation completed KIO !
- 13.Jun.95 All opcodes covered, no unsupported opcodes! KIO !
- */
-
- #include "kio.h"
- #include "z80.options"
- #include "z80.h"
- #include "profiler.h"
- #include "z80_opcodes.h"
- #include "Z80.ppc.macros"
-
-
- #if GENERATINGPOWERPC
- #define EI_WITH_DELAY true // emulate one instruction delay after ei
- #define COMPACT_CODE true // compact code is faster anyway (more cache hits ?!)
- #define REFERENCE_ENGINE false // true: use REF_CORE, ref_zreg and do dummy i/o
-
- #else
- #define EI_WITH_DELAY true
- #define COMPACT_CODE true // generate shorter code to fit in a 32k segment
- #define REFERENCE_ENGINE false
- #endif
-
-
- #if REFERENCE_ENGINE
- #define zreg ref_zreg // 2nd register set for Z80_Ref()
- #define CORE REF_CORE // 2nd core for Z80_Ref()
- #define Do_Output(A,B) No_Output(A,B) // ignore out's of Z80_Ref()
- #define Do_Input(A) Do_Input(A) // note: don't poll events between input of Z80_T() and Z80_Ref()
- #define write_to_rom(A,B) Dont_Write(A,B) // don't write to ROM; Z80_T() shouldn't write to ROM too
- #endif
-
-
- /* ---- The Z80 Engine -------------------------------------------------------
-
- Z80_PPC()
-
- Features:
- • all legal instructions
- • all known illegal instructions (nearly no opcode gaps)
- • all flags (8 bit)
- • exact T cycle counting
- -> exact speed
- -> timing loop depending sound
- -> timing loop depending tape i/o
- -> highres screen update
- • ROM write protection (except stack operations)
- • exact R register emulation
- • 1 instruction latency after EI
- • Info() calls for all illegal and some interesting instructions
- • instruction and address profiling
- • Single stepping
-
- global struct zreg keeps z80 registers on entry/return of Z80_PPC()
- some registers are copied to local variables while Z80_PPC() is running
- global pointer CORE points to 0x10000 bytes of memory to use as z80 memory
- macro rompoke(A,N) and rompoke2(A,nn) provide means for ROM write protection
-
- Z80_PPC() does this:
- 1. process NMI, if NMI is pending
- 2. process interrupt, if IRPT is pending and not disabled
- 3. execute z80 instructions, until T cycle counter expires
- IRPTs are only tested again after an EI instruction
- 4. if T cycle counter reaches 0, Do_Cycles() is called and may
- • reload the T cycle counter
- • trigger NMI or IRPT
- • require Z80_PPC() to exit
-
- If Z80() returns, it's result value indicates one of the following conditions:
- • EXIT was set by Do_Cycles()
- • not supported instruction at zreg.IP
- • halt instruction executed at zreg.IP-1
- • rst0 instruction executed at zreg.IP-1
- • illegal interrupt mode / not supported instruction in interrupt mode 0
-
- Single instructions may be executed by calling Z80_PPC() with CYCLES=4
- Then one of the following is processed: (in priority order)
- • handle nmi request
- • handle irpt request (if interrupts are enabled)
- • execute one instruction, except:
- • if instruction is EI, handle EI and next instruction in one go
- */
-
-
- // ---- jumping -----------------------------------------------------------
- #define loop goto nxtcmnd // loop to next instruction
- #define exit(N) { c=N; goto exit_z80; } // exit Z80()
-
-
- // ---- load and store local variables from/to zreg struct ------------
- #define load_registers \
- load_r; /* refresh counter R */ \
- cc = CYCLES; /* T cycle counter */ \
- pc = PC; /* program counter PC */ \
- sp = SP; /* stack pointer SP */ \
- ra = RA; /* register A */ \
- rf = RF; /* register F */
-
- #define save_registers \
- store_r; /* refresh counter R */ \
- CYCLES = cc; /* T cycle counter */ \
- PC = pc; /* program counter PC */ \
- SP = sp; /* stack pointer SP */ \
- RA = ra; /* register A */ \
- RF = rf; /* register F */
-
-
- // ==== The Z80 ENGINE ====================================================
- short Z80_PPC()
- {
- register Short pc; // z80 program counter
- register Short sp; // z80 stack pointer
- register Char ra; // z80 a register
- register Char rf; // z80 flags
-
- register long cc; // T cycle counter
- #define sto_cc CYCLES = cc
- #define ld_cc cc = CYCLES
- #define time(N) cc-=N-4 // T cycles for instruction
- #define more(N) cc-=N // adjust T cycles after use of time()
-
- register dreg *izp;
- #define iz izp->rr
- #define rzh izp->r.hi
- #define rzl izp->r.lo
-
- register Char c; // general purpose byte register
-
- register Short w; // general purpose word register
- #define wl (Char)w // access low byte of w
- #define wh (w>>8) // access high byte of w
-
- register Short wm; // help register for macro internal use
- #define wml (Char)wm // access low byte of wm
- #define wmh (wm>>8) // access high byte of wm
-
- #if EI_WITH_DELAY
- register long ccx = 0; // limit for cc
- #else
- #define ccx 0
- #endif
-
- #if EXACT_R
- register Char r; // r register bit 0...6
- #endif
-
-
- load_registers;
-
-
- // check WUFF to see, whether an NMI or IRPT is pending:
- // also called after ei processing!
- check_wuff:
- if (WUFF)
- { if (WUFF&0x80) // NMI: 0x0066; 11 T cycles
- { do_info_nmi;
- WUFF&=0x7F;
- IFF2=IFF1;
- IFF1=disabled;
- push(pc);
- pc = 0x0066;
- more(11);
- loop;
- }
- if (IFF1==enabled) // interrupts enabled?
- { do_info_irpt;
- WUFF--; // clear interrupt request
- IFF1=IFF2=disabled; // disable iff1 & iff2
- switch (IM)
- {
- case 0: // mode 0: read instr; 1 T cycle + normal instr timing
- // if (IRPTCMD==RST38)
- // { push(pc);
- // pc = 0x0038;
- // more(12); // RST==11T + 1T
- // loop;
- // };
- // exit(irpt_error); // not supported instruction read in irpt ackn cycle
- case 1: // mode 1: rst 56; 13 T cycles
- push(pc);
- pc = 0x0038;
- more(13);
- loop;
- case 2: // mode2: jmp via table; 19 T cycles
- push(pc);
- w = IRPTVEK;
- pc = ((Short)peek(w+1)<<8) + peek(w);
- more(19);
- loop;
- default:
- exit(irpt_error);
- };
- };
- };
-
- // ----- THE MAIN INSTRUCTION DISPATCHER --------------------------------
-
- nxtcmnd:
- if ((cc-=4)>=ccx) // -4 T cycles for most frequent instructions
- {
- #include "Codes.c"
- }
- else
- { cc+=4;
- #if EI_WITH_DELAY
- if (ccx) { ccx=0; goto check_wuff; } // for correct ei handling
- #endif
- sto_cc;
- Do_Cycles();
- ld_cc;
- if (!EXIT) goto check_wuff;
- exit(watchdog_irpt);
- };
-
-
- // ----- Exit points -------------------------------------------------------------
-
- #if INFO_ILLEGALS
- ill2: exit(ill_instr2);
- ill3: exit(ill_instr3);
- ill4: exit(ill_instr4);
- #endif
-
- #if INFO_WEIRD
- weird1: exit(weird_instr1);
- weird2: exit(weird_instr2);
- weird3: exit(weird_instr3);
- weird4: exit(weird_instr4);
- #endif
-
-
- exit_z80: // write back registers and return
- save_registers;
- return c; // result code
- }
-
-
-
-